from flask import (Blueprint,
                   request,
                   render_template,
                   current_app,
                   make_response,
                   session,
                   redirect,
                   g,
                   jsonify,
                   url_for)
import string
import random
from utils import restful
from utils.captcha import Captcha
import time
import os
from io import BytesIO
from .forms import RegisterForm, LoginForm, UploadForm, EditProfileForm, PublicPostForm, PublicCommentForm
from models.auth import UserModel, Permission
from exts import db, cache
from .decorators import login_required
from hashlib import md5
from flask_avatars import Identicon
from models.post import BoardModel, PostModel, CommentModel, BannerModel
from flask_paginate import get_page_parameter, Pagination
from sqlalchemy.sql import func
from flask_jwt_extended import create_access_token

bp = Blueprint('front', __name__, url_prefix='/')


@bp.before_request
def front_before_request():
    if 'user_id' in session:
        user_id = session.get("user_id")
        user = UserModel.query.get(user_id)
        setattr(g, "user", user)


# 上下文处理器
@bp.context_processor
def front_context_processor():
    if hasattr(g, 'user'):
        return {"user": g.user}
    else:
        return {}


@bp.get("/email/captcha")
def email_captcha():
    # /email/captcha?email=xx@xx.com
    email = request.args.get("email")
    if not email:
        return restful.params_error(message="请先传入邮箱！")
    source = list(string.digits)
    captcha = "".join(random.sample(source, 6))
    subject = "【知识论坛】注册验证码"
    body = f"【知识论坛】您的注册验证码为{captcha}"
    current_app.celery.send_task("send_mail", (email, subject, body))
    cache.set(email, captcha)
    return restful.ok(message="邮件发送成功！")


@bp.route("/graph/capthca")
def graph_capthca():
    captcha, image = Captcha.gene_graph_captcha()
    key = md5((captcha + str(time.time())).encode('utf-8')).hexdigest()
    cache.set(key, captcha)
    out = BytesIO()
    image.save(out, 'png')
    out.seek(0)  # out 文件指针归位
    resp = make_response(out.read())
    resp.content_type = "image/png"
    resp.set_cookie("_graph_captcha_key", key, max_age=3600)
    return resp


@bp.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'GET':
        return render_template("front/login.html")
    else:
        form = LoginForm(request.form)
        if form.validate():
            email = form.email.data
            password = form.password.data
            remember = form.remember.data
            user = UserModel.query.filter_by(email=email).first()
            if not user:
                return restful.params_error("邮箱或密码错误！")
            if not user.check_password(password):
                return restful.params_error("邮箱或密码错误！")
            session['user_id'] = user.id
            permissions = []
            if user.is_staff:
                token = create_access_token(identity=user.id)
                for attr in dir(Permission):
                    if not attr.startswith("_"):
                        permission = getattr(Permission, attr)
                        if user.has_permission(permission):
                            permissions.append(attr.lower())
            if remember == 1:
                session.permanent = True
            user = user.to_dict()
            user['permissions'] = permissions
            return restful.ok(data={"token": token, "user": user})
        else:
            return restful.params_error(message=form.messages[0])


@bp.route('/register', methods=['GET', 'POST'])
def register():
    if request.method == 'GET':
        return render_template("front/register.html")
    else:
        form = RegisterForm(request.form)
        if form.validate():
            email = form.email.data
            username = form.username.data
            password = form.password.data
            identicon = Identicon()
            filenames = identicon.generate(text=md5(email.encode('utf-8')).hexdigest())
            avatar = filenames[2]
            user = UserModel(email=email, username=username, password=password, avatar=avatar)
            db.session.add(user)
            db.session.commit()
            return restful.ok()
        else:
            message = form.errors[0]
            return restful.params_error(message=message)


@bp.route('/', methods=['GET'])
def index():
    sort = request.args.get('st', type=int, default=1)
    board_id = request.args.get("bd", type=int, default=None)
    boards = BoardModel.query.order_by(BoardModel.priority).all()
    if sort == 1:
        posts_query = PostModel.query.order_by(PostModel.create_time.desc())
    else:
        posts_query = (db.session.query(PostModel)
                       .outerjoin(CommentModel)
                       .group_by(PostModel.id)
                       .order_by(func.count(CommentModel.id).desc(), PostModel.create_time.desc()))

    page = request.args.get(get_page_parameter(), type=int, default=1)
    start = (page - 1) * current_app.config['PER_PAGE_COUNT']
    end = start + current_app.config['PER_PAGE_COUNT']
    if board_id:
        posts_query = posts_query.filter(PostModel.board_id == board_id)
    total = posts_query.count()
    posts = posts_query.slice(start, end)
    pagination = Pagination(bs_version=3, page=page, total=total)
    banners = BannerModel.query.order_by(BannerModel.priority.desc()).all()
    context = {
        'boards': boards,
        'posts': posts,
        'pagination': pagination,
        'st': sort,
        'bd': board_id,
        'banners': banners
    }
    return render_template('front/index.html', **context)


@bp.route('/logout')
def logout():
    session.clear()
    return redirect('/')


@bp.route('/setting')
@login_required
def setting():
    return render_template('front/setting.html')


@bp.post("/avatar/upload")
@login_required
def update_avatar():
    form = UploadForm(request.files)
    if form.validate():
        image = form.image.data
        filename = image.filename
        # xxx.png,xx.jpeg
        _, ext = os.path.splitext(filename)
        filename = md5((g.user.email + str(time.time())).encode("utf-8")).hexdigest() + ext
        image_path = os.path.join(current_app.config['AVATARS_SAVE_PATH'], filename)
        image.save(image_path)
        g.user.avatar = filename  # 上传后立即修改
        db.session.commit()
        return restful.ok(data={"avatar": filename})
    else:
        message = form.messages[0]
        return restful.params_error(message=message)


@bp.post("/profile/edit")
@login_required
def edit_profile():
    form = EditProfileForm(request.form)
    if form.validate():
        signature = form.signature.data
        g.user.signature = signature
        db.session.commit()
        return restful.ok()
    else:
        message = form.messages[0]
        return restful.params_error(message=message)


@bp.route("/post/public", methods=['GET', 'POST'])
@login_required
def public_post():
    if request.method == "GET":
        boards = BoardModel.query.order_by(BoardModel.priority).all()
        return render_template("front/public_post.html", boards=boards)
    else:
        form = PublicPostForm(request.form)
        if form.validate():
            title = form.title.data
            content = form.content.data
            board_id = form.board_id.data
            try:
                board = BoardModel.query.get(board_id)
            except Exception as e:
                raise restful.params_error('板块数据错误！')
            post_model = PostModel(title=title, content=content, board=board, author=g.user)
            db.session.add(post_model)
            db.session.commit()
            return restful.ok(data={'id': post_model.id})
        else:
            message = form.messages[0]
            return restful.params_error(message=message)


@bp.post("/post/image/upload")
@login_required
def upload_post_image():
    """
        可以与update_avatar重构
    """
    form = UploadForm(request.files)
    if form.validate():
        image = form.image.data
        filename = image.filename
        # xxx.png,xx.jpeg
        _, ext = os.path.splitext(filename)
        filename = md5((g.user.email + str(time.time())).encode("utf-8")).hexdigest() + ext
        image_path = os.path.join(current_app.config['POST_IMAGE_SAVE_PATH'], filename)
        image.save(image_path)
        return jsonify({"errno": 0, "data": [{
            "url": url_for("media.get_post_image", filename=filename),
            "alt": filename,
            "href": ""
        }]})
    else:
        message = form.messages[0]
        return restful.params_error(message=message)


@bp.get('/post/detail/<int:post_id>')
def post_detail(post_id):
    try:
        post_model = PostModel.query.get(post_id)
    except:
        return '404'
    comment_count = CommentModel.query.filter_by(post_id=post_id).count()
    context = {
        "post": post_model,
        "comment_count": comment_count
    }
    return render_template('front/post_detail.html', **context)


@bp.post("/comment")
@login_required
def public_comment():
    form = PublicCommentForm(request.form)
    if form.validate():
        content = form.content.data
        post_id = form.post_id.data
        try:
            post_model = PostModel.query.get(post_id)
        except Exception as e:
            return restful.params_error("帖子不存在！")
        comment = CommentModel(content=content, post_id=post_id, author_id=g.user.id)
        db.session.add(comment)
        db.session.commit()
        return restful.ok()
    else:
        message = form.messages[0]
        return restful.params_error(message=message)


@bp.get('/cms')
def cms():
    return render_template('cms/index.html')
